﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace AsyncDemo
{
    public partial class asyncMain : Form
    {
        public asyncMain()
        {
            InitializeComponent();
        }

        public void WriteJob(string name)
        {
            Console.WriteLine("********************** " + name + " Start【" + Thread.CurrentThread.ManagedThreadId + "】等待............... ***");
            Stopwatch watch = new Stopwatch();
            watch.Start();
            for (int i = 0; i < 10; i++)
            {
                Thread.Sleep(32);
                Console.WriteLine($"学生:{name}在做第{i + 1}题作业");
            }
            watch.Stop();
            Console.WriteLine("********************** " + name + " End【" + Thread.CurrentThread.ManagedThreadId + "】 用时" + watch.ElapsedMilliseconds + "毫秒............... ***");
        }
        /// <summary>
        /// 读书,一个很废时间的任务
        /// </summary>
        public void ReadBook()
        {
            //我们可以通过 Thread.CurrentThread.ManagedThreadId 获取当前线程的唯一标识符
            Console.WriteLine("********************** ReadBook Start【" + Thread.CurrentThread.ManagedThreadId + "】等待............... **********************************************");
            System.Diagnostics.Stopwatch watch = new System.Diagnostics.Stopwatch();
            watch.Start();
            string Path = AppDomain.CurrentDomain.BaseDirectory + "zqjz.txt";
            List<string> list = new List<string>();
            System.IO.StreamReader sr = new System.IO.StreamReader(Path, Encoding.Default);
            string line = "";
            Console.ForegroundColor = ConsoleColor.Black;
            while ((line = sr.ReadLine()) != null && list.Count <= 5)
            {
                char[] array = line.ToArray();
                for (int i = 0; i < array.Length; i++)
                {

                    Console.Write(array[i]);
                    if (i != 0)
                    {
                        //  Thread.Sleep(128);//人眼最快敏感视觉是128毫秒左右，我们这里测试先使用10毫秒
                        Thread.Sleep(10);
                    }
                }
                Console.WriteLine();
                Console.BackgroundColor = (ConsoleColor)new Random().Next(3, 15);
                list.Add(line);
            }
            sr.Close();
            sr.Dispose();
            watch.Stop();
            Console.WriteLine("今天读书用了" + watch.ElapsedMilliseconds + "豪秒");
            Console.WriteLine("********************** ReadBook End【" + Thread.CurrentThread.ManagedThreadId + " 】**********************************************");
        }
        private void btnSync_Click(object sender, EventArgs e)
        {//同步
            Console.WriteLine("**********************btnSync_Click Start【" + Thread.CurrentThread.ManagedThreadId + "】**********************************************");
            ReadBook();
            MessageBox.Show("今天书就读到这里吧！！累了，休息一会，休息一会！一休哥");
            Console.WriteLine("**********************btnSync_Click End 【" + Thread.CurrentThread.ManagedThreadId + "】**********************************************");
        }
        private void btnasync_Click(object sender, EventArgs e)
        {//异步
            Console.WriteLine("**********************btnSync_Click Start【" + Thread.CurrentThread.ManagedThreadId + "】**********************************************");
            #region  异步回调
            IAsyncResult iAsyncResult = null;
            string state = "RunState";
            AsyncCallback callback = t =>
            {
                Console.WriteLine(t.AsyncState);
                Console.WriteLine(state);
                Console.WriteLine(t);
                Console.WriteLine("下边代码是比较两个对象是否一样");
                Console.WriteLine($"string.ReferenceEquals(t, iAsyncResult)={string.ReferenceEquals(t, iAsyncResult)}");
                Console.WriteLine($"当前线程ID 【{Thread.CurrentThread.ManagedThreadId}】");
                Console.WriteLine($"终于下课了！我们走吧，尽情的玩吧，你问我老师讲的啥，我知道！！");
            };//AsyncCallback 本身就是一个委托，这个委托有一个参数，这个参数就是我们委托的BeginInvoke的返回值。我们使用这个委托去做异步回调
            #endregion
            Action action = () => ReadBook();//简写
            iAsyncResult = action.BeginInvoke(callback, "RunState");//这里的第一个参数，我们就是异步回调
            MessageBox.Show("今天想玩，怎么骗过老师呢,下课玩点什么呢！！书还在继续读，但是我的心已经飞了!!!");
            #region 异步等待1 有损耗 带返回
            //while (!iAsyncResult.IsCompleted)//边等待边操作，可以用于做进度条
            //{
            //    Thread.Sleep(100);//建议控制100毫秒一次
            //    Console.WriteLine("老师还在教我们读书.....请等待...............");
            //       //就这样一直阻塞下去
            //}
            #endregion
            #region 异步等待2 无损耗 无返回
            //bool RunBool = iAsyncResult.AsyncWaitHandle.WaitOne();//返回结果是子线程执行成功或者失败，不是实时返回的。
            //iAsyncResult.AsyncWaitHandle.WaitOne(-1);//写法2  阻塞写法2 
            #endregion
            #region 有时间限制的异步等待
            //iAsyncResult.AsyncWaitHandle.WaitOne(2000);//我只等你两秒钟，主线程最多阻塞2两秒，如果子线程提前完事，我也就不阻塞了
            #endregion
            #region 异步等待死等
            //死等就是，只要你不异常，就必须给我一个结果，比如学习，必须学会为止，学不会直到累死。我们这里主线程就会阻塞到死
            // action.EndInvoke(iAsyncResult);//EndInvoke 的返回值取决与你的委托返回值，委托有返回值EndInvoke就有返回值。
            #endregion
            //当异步完成以后，我们在执行下边的这句话
            Console.WriteLine("学生甲：冲啊..............打篮球全");
            Console.WriteLine("学生乙：王美美.......我爱你！咱们交往吧....*#*#*#**??!");
            Console.WriteLine("学生丙：呼呼呼呼呼呼呼呼。。。。。噜。。。。。。。。。。今天的肉丸子真好吃，真希望这不是梦啊");
            Console.WriteLine("学生丁：大海啊，就像妈妈一样，海浪啊！你为啥这么猛！总是在我人生巅峰......被打断");
            Console.WriteLine("学生丙：别BiBi了，海浪是你后妈，滚一边去淫诗去！别TMD打扰老子睡觉");
            Console.WriteLine("**********************btnSync_Click End 【" + Thread.CurrentThread.ManagedThreadId + "】**********************************************");
            Console.WriteLine("**********************btnSync_Click End 【" + Thread.CurrentThread.ManagedThreadId + "】**********************************************");
            Console.WriteLine("**********************btnSync_Click End 【" + Thread.CurrentThread.ManagedThreadId + "】**********************************************");
            Console.WriteLine("**********************btnSync_Click End 【" + Thread.CurrentThread.ManagedThreadId + "】**********************************************");
            Console.WriteLine("**********************btnSync_Click End 【" + Thread.CurrentThread.ManagedThreadId + "】**********************************************");
        }

        private void asyncMain_Load(object sender, EventArgs e)
        {

        }

        /// <summary>
        /// 异步多线程三大特点:
        /// 1 同步卡界面，UI线程被占用；异步多线程不卡界面，UI线程空闲，计算任务交给子线程
        /// 2 同步方法慢，因为只有一个线程干活；异步多线程方法快，因为多个线程并发计算,
        ///   这里也会消耗更多的资源，不是线程的线性关系，不是线程越多越好(1资源有限 2线程调度耗资源 3不稳定)
        /// 3 异步多线程是无序的，不可预测的：启动顺序不确定、消耗时间不确定、结束顺序不确定
        ///   我们不要试图控制执行的顺序
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void button1_Click(object sender, EventArgs e)
        {
            // Thread.CurrentThread.ManagedThreadId 获取线程的ID ,我们尽量不要使用  Thread.CurrentThread.Name,因为name 并不是唯一的
            Console.WriteLine("**********************button1_Click Start【" + Thread.CurrentThread.ManagedThreadId + "】**********************************************");
            Thread t = new Thread(() =>
            {

                for (int i = 0; i < 100; i++)
                {
                    // Thread.Sleep(100);//演示前台线程和后台线程，演示线程无序性质，请注释掉
                    Console.WriteLine($"我是子线程({i})~~~~");
                }

            });// 参数 ThreadStart 是一个委托类型的。凡是委托，我们都可以使用lambda 表达式代替
               // t.IsBackground=true;//设置为true 的时候为后台线程，默认为false前台线程。关闭窗体会立即停止计算。
               // t.ThreadState 只读属性获取当前线程的状态
               //t.IsAlive 当前线程的运行状态
            t.Start();//Start 通过Start开启一个线程

            for (int i = 0; i < 100; i++)
            {
                Console.WriteLine($"我是主线程【{i}】");
            }
            /*
             * 线程状态操作，建议不要使用，微软自带的有bug ,并且已经过时了，如果非要暂停和回复，建议自己写算法去实现。
            t.Suspend();//暂停
            t.Resume();//恢复
            */
            // t.Abort();//销毁线程。
            t.Join();//线程等待，可以使用，
        }
        /// <summary>
        /// 线程等待
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void button2_Click(object sender, EventArgs e)
        {
            Console.WriteLine("**********************button2_Click Start【" + Thread.CurrentThread.ManagedThreadId + "】**********************************************");

            List<Thread> threadList = new List<Thread>();
            for (int i = 0; i < 5; i++)
            {
                string studentName = "甲" + i + "同学";
                Thread thread = new Thread(() => WriteJob(studentName));
                Console.WriteLine($"{studentName}开始写作业");
                thread.Start();
                threadList.Add(thread);
            }

            //无损耗阻塞主线程
            //foreach (var thread in threadList)
            //{
            //    thread.Join();//表示把thread线程任务join到当前线程，也就是当前线程等着thread任务完成
            //}
            //带返回，有损耗阻塞主线程
            while (threadList.Count(t => t.ThreadState != System.Threading.ThreadState.Stopped) > 0)
            {
                Thread.Sleep(100);
                Console.WriteLine("请等待....");
            }

            Console.WriteLine("**********************button2_Click end【" + Thread.CurrentThread.ManagedThreadId + "】**********************************************");
        }

        /// <summary>
        /// 异步回调与现场回调
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void button3_Click(object sender, EventArgs e)
        {
            #region 异步回调回顾
            /*
            {
                //回调
                AsyncCallback callBack = param =>
                {
                    Console.WriteLine("当前状态:"+param.AsyncState);
                    Console.WriteLine("你睡吧，我去吃好吃的去了");
                };
                //有返回，返回毫秒数
                Func<string, int> func = t => {
                    
                    Console.WriteLine(t);
                    Thread.Sleep(5000);
                    Console.WriteLine("等等吧，我在睡一会");
                    return DateTime.Now.Millisecond;//返回当前毫秒数
                };
                ///第一个参数是我们自定义的参数，第二个参数是我们回调的参数，第三个参数是状态参数
               IAsyncResult iAsyncResult= func.BeginInvoke("张四火，起床吧", callBack, "runState");
               int second= func.EndInvoke(iAsyncResult);
                Console.WriteLine("当前毫秒数："+second);
            }
            */
            /*
            {
                //回调
                AsyncCallback callBack = param =>
                {
                    Console.WriteLine("当前状态:" + param.AsyncState);
                    Console.WriteLine("妈妈说：" + param.AsyncState + "太小，睡会吧");
                };
              // 异步 无返回
                Action<string> act = t =>
                {
                    Console.WriteLine(t);
                    Thread.Sleep(5000);
                    Console.WriteLine("等等吧，我在睡一会");
                   
                };
                IAsyncResult iAsyncResult = act.BeginInvoke("梓烨，起床吧", callBack, "梓烨");
                 act.EndInvoke(iAsyncResult);
            }*/
            #endregion
            #region 多线程回调封装调用
            {
                //无返回
                ThreadWithCallback(() =>
                {
                    Console.WriteLine("梓烨，起床吧");
                    Thread.Sleep(5000);
                    Console.WriteLine("等等吧，我在睡一会");
                }, () =>
                {
                    Console.WriteLine("妈妈说：梓烨太小，睡会吧");
                });
            }
            {
                //有返回
                int secound = ThreadWithReturn<int>(() =>
                {
                    Console.WriteLine("张四火，起床吧");
                    Thread.Sleep(5000);
                    Console.WriteLine("等等吧，我在睡一会");
                    return DateTime.Now.Millisecond;//返回当前毫秒数
                }).Invoke();
                Console.WriteLine(secound);
            }
            #endregion

        }
        #region 回调封装
        /// <summary>
        /// 回调封装 无返回值
        /// </summary>
        /// <param name="start"></param>
        /// <param name="callback">回调</param>
        private void ThreadWithCallback(ThreadStart start, Action callback)
        {
            Thread thread = new Thread(() =>
            {
                start.Invoke();
                callback.Invoke();
            });
            thread.Start();
        }

        /// <summary>
        /// 有返回值封装（请根据本案例自行封装回调）
        /// </summary>
        /// <typeparam name="T">返回值类型</typeparam>
        /// <param name="func">需要子线程执行的方法</param>
        /// <returns></returns>
        private Func<T> ThreadWithReturn<T>(Func<T> func)
        {
            T t = default(T);//初始化一个泛型
            ThreadStart newStart = () =>
            {
                t = func.Invoke();

            };
            Thread thread = new Thread(newStart);
            thread.Start();

            return new Func<T>(() =>
            {
                thread.Join();
                return t;
            });
        }
        #endregion

        private void button4_Click(object sender, EventArgs e)
        {
            FrmAsync frm = new FrmAsync();
            frm.ShowDialog();
        }

        private void button5_Click(object sender, EventArgs e)
        {
            #region 设置初始化线程
            //ThreadPool.SetMaxThreads(8, 8);//最小也是CPU核数
            //ThreadPool.SetMinThreads(4, 4);
            #endregion 
            #region 获取线程池当前设置 ，默认设置取决于操作系统和CPU 
            //int workerThreads = 0;
            //int ioThreads = 0;
            //ThreadPool.GetMaxThreads(out workerThreads, out ioThreads);
            //Console.WriteLine(String.Format("可创建最大线程数: {0};    最大 I/O 线程: {1}", workerThreads, ioThreads));

            //ThreadPool.GetMinThreads(out workerThreads, out ioThreads);
            //Console.WriteLine(String.Format("最小线程数: {0};    最小 I/O 线程: {1}", workerThreads, ioThreads));

            //ThreadPool.GetAvailableThreads(out workerThreads, out ioThreads);
            //Console.WriteLine(String.Format("可以使用的工作线程: {0};    可用 I/O 线程: {1}", workerThreads, ioThreads));
            #endregion
            #region 多线程的使用
            Console.WriteLine("当前主线程id:{0}", Thread.CurrentThread.ManagedThreadId);
           
            //首先创建5个任务线程
            ManualResetEvent[] mre = new ManualResetEvent[]
            {
                new ManualResetEvent(false),
                new ManualResetEvent(false),
                new ManualResetEvent(false),
                new ManualResetEvent(false),
                new ManualResetEvent(false)
            };
            for (int i = 0; i < 5; i++)
            {
                //false 默认是关闭的，TRUE 默认为打开的
                Thread.Sleep(300);
                ThreadPool.QueueUserWorkItem(t =>
                {
                    //lambda任务

                    Console.WriteLine($@"参数的内容是{t}");
                    Console.WriteLine("获取参数值((dynamic)t).num：" + ((dynamic)t).num);
                    int num = ((dynamic)t).num;
                    for (int j = 0; j < num; j++)
                    {
                        Thread.Sleep(2);//一件很耗时的事情
                    }
                    Console.WriteLine(@"当前子线程id:{0} 的状态:{1}", Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.ThreadState);

                    //这里是释放共享锁，让其他线程进入
                    mre[i].Set();//可以理解为打开一个线程
                    //这里是打开共享锁，让其他线程进入
                    // mre[i].Reset();//可以理解为关闭一个线程
                    Console.WriteLine();
                }, new { num = (i + 1) * 10 });
                mre[i].WaitOne(3000); //单独阻塞线程，因为我们使用的是池，索引这里不使用这个，用法同委托异步
            }
            //注意这里，设定WaitAll是为了阻塞调用线程（主线程），让其余线程先执行完毕，
            //其中每个任务完成后调用其set()方法(收到信号),当所有
            //的任务都收到信号后，执行完毕，将控制权再次交回调用线程（这里的主线程）
           // ManualResetEvent.WaitAll(mre);不建议这么使用

            Console.ReadKey();
            #endregion
        }
    }
}
